<?php
Abstract class Model extends RecordObject{
	public static $dbo = array();

	/**
	 * Get All rows or some rows
	 **/
	public function get($id=null){
		if($id){
			$data = self::$dbo['r']->findInstance(get_class($this), $id);
		}else{
			$data =  self::$dbo['r']->find($this->getTableName());
		}
		return $data;
	}

	/**
	 * Delete some rows
	 **/
	public function del($id){
		return self::$dbo['w']->deleteInstance($this->getTableName(),$id);
	}

	/**
	 * execute sql
	 * e.g: 
	 *  insert:
	 *	$sql   = 'select * from table_a where name = :name';
	 *	$param = array(':name'=>$name);
	 *	$array = selectBySql($sql,$param);
	 */
	public static function selectBySql($sql,array $param=array()){
		$dbo = self::$dbo['r']->getDbHandle();
		try
		{
			$smt      = $dbo->prepare($sql);
			$exec_ret = $smt->execute($param);
		}
		catch(Exception $e)
		{
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - SQL FAILS: $sql");
			error_log("Parameters: " . print_r($param, TRUE));
			error_log(print_r($smt->errorInfo(), TRUE));
			error_log("Exception Message: ".$e->getMessage());
			//throw $e;
			return array();
		}
		return $smt->fetchAll(PDO::FETCH_ASSOC);
	}

	/**
	 * execute UPDATE/DELETE/INSERT sql
	 * e.g: 
	 *  insert:
	 *	$sql   = 'insert into table_a(name) value (:name)';
	 *	$param = array(':name'=>$name);
	 *	$insert_id = updateBySql($sql,$param);
	 *
	 *	update:
	 *	$sql   = 'update table_a set name=:name';
	 *	$param = array(':name'=>$name);
	 *	$uploaded_rows = updateBySql($sql,$param);
	 */
	public static function updateBySql($sql,$param=array()){
		$is_insert = strtolower(substr(trim($sql),0,6))=='insert';
		$dbo       = self::$dbo['w']->getDbHandle();

		try
		{
			$smt      = $dbo->prepare($sql);
			$exec_ret = $smt->execute($param);

		}
		catch(Exception $e)
		{
			error_log("ERROR CLASS:" . __CLASS__ . ", FUNCTION: " . __FUNCTION__ . ", LINE: " . __LINE__);
			error_log("ERROR - SQL FAILS: $sql");
			error_log("Parameters: " . print_r($param, TRUE));
			error_log(print_r($smt->errorInfo(), TRUE));
			error_log("Exception Message: ".$e->getMessage());
			throw $e;
		}

		if($is_insert){
			return $dbo->lastInsertId();
		}else{
			return $smt->rowCount();
		}
	}


	public static function diff($instance,$object){
		foreach($object as $k=>$v){
			if(property_exists($instance,$k)&&$instance->$k !=$v){
				$instance->$k = is_null($v) ? '' : $v;
			}
			elseif (property_exists($instance,$k)&&$instance->$k == null)
			{
				$instance->$k = '';
			}
		}
		return $instance;
	}

    /**
     * proofread SQL get data!
     * @param   $tableName  String
     * @param   $whereArray Array
     * @return  String
     */
    public static function assembleGetProofreadSql($tableName, $whereArray)/*{{{*/
    {
        if (!is_array($whereArray)) return  NULL;

        $where  =   implode(' AND ', $whereArray);
        $sql    =   'SELECT * FROM `'. $tableName. '` WHERE '. $where;
        return  $sql;
    }/*}}}*/

    /**
     * proofread SQL insert data!
     * @param   $dataArray  Array
     * @return  Array
     */
    public static function assembleInsertProofreadSql($dataArray,array $duplicate_update_array=array(),$dbo=null)/*{{{*/
    {
        if (empty($dataArray)   ||  !is_array($dataArray))  return  NULL;

        $insertArray    =   NULL;
        foreach ($dataArray as  $table  =>  $data)
        {
            if (empty($data))
            {
                $insertArray[$table]=   '';
                continue;
            }
			$fields = '`'.implode('`,`',CommonUtils::get_table_fields($table,$dbo)).'`';
            $insertArray[$table]    =   "INSERT INTO `$table`($fields) VALUES ". implode(',', $data);
			if(isset($duplicate_update_array[$table])){
				$insertArray[$table] .= $duplicate_update_array[$table];
			}
        }

        return  $insertArray;
    }/*}}}*/

	/**
	 * assemble where sentence
	 *
	 * @param	$find		Array or String
	 * @param	$field		String
	 * @param	$value		String
	 * @param	$operation	String
	 * @return	String
	 */
	public static function assembleWhereSentence($find, $field, $operation, $value)/*{{{*/
	{
		if (empty($field)	||	empty($operation))	return	NULL;
		if (is_array($field))
		{
			$where	=	$array	=	$tmp	=	NULL;
			foreach	($field	as	$key =>	$vField)
			{
                $valueKey   =   trim($value[$key]);
                if ('record_status'  ==  $vField)
                {
					# append by vanni at 2009-10-14
					$valueKey = strtolower($valueKey);

					$tmp = '';
					switch($operation[$key]){
						case 'bw': # in
						case 'ew':
						case 'cn':
						case 'ne': # not in
							$ids  = '';
							$opt  = ($operation[$key] == 'ne') ? ' not in' : ' in';
							$klen = strlen($valueKey);
							foreach(AC::$op_status2id as $full_key=>$statusid){
								$found = strpos($full_key,$valueKey);
								if($found!==false){
									if($operation[$key]=='bw' && $found===0){
										$ids .= $statusid.',';
									}elseif($operation[$key]=='ew' && $found+$klen == strlen($full_key)){
										$ids .= $statusid.',';
									}elseif($operation[$key]=='ne' && $klen == strlen($full_key)){
										$ids .= $statusid.',';
									}elseif($operation[$key]=='cn'){
										$ids .= $statusid.',';
									}
								}
							}
							$ids = substr($ids,0,-1);
							if($ids){
								$tmp = ' '.$find['record_status'].$opt.'('.$ids.')';
							}else{
								$tmp = ' 1<>1 ';
							}
						break;
						case 'eq':
							if(isset(AC::$op_status2id[$valueKey])){
								$tmp = ' '.$find['record_status'].'='.AC::$op_status2id[$valueKey];
							}else{
								$tmp = ' 1<>1 ';
							}
						break;
					}
                }else{
					$tmp = self::assembleWhereSentence($find, $vField, $operation[$key], $valueKey);
				}
				if (!empty($tmp))	$array[]=	$tmp;
			}
			# append.

			if (is_array($array))	$where	=	' ('. implode(") AND (", $array). ') ';
			return	$where;
		}
		else
		{
			if (empty($find[$field]))
			{
				#exit;
				return	NULL;
			}
			$where	=	' '. $find[$field];
            $value  =   trim($value);
			if(!get_magic_quotes_gpc()){
				$value = str_replace("'","\'",$value);
			}
			#if ('title'	==  $field)
			#{
			#	if ('ne' !=	$operation)	$operation	=	'cn';
			#}

			switch ($operation)
			{
				case 'bw':	$where	.=	' like \''.	$value. '%\'';	break; 
				case 'eq':	$where	.=	' = \''.	$value. '\'';	break;
				case 'ne':	$where	.=	' <> \''.	$value. '\'';	break;
				case 'lt':	$where	.=	' < \''.	$value. '\'';	break;
				case 'le':	$where	.=	' <= \''.	$value. '\'';	break;
				case 'gt':	$where	.=	' > \''.	$value. '\'';	break;
				case 'ge':	$where	.=	' >= \''.	$value. '\'';	break;
				case 'ew':	$where	.=	' like \'%'.$value. '\'';	break;
				case 'cn':	$where	.=	' like \'%'.$value. '%\'';	break;
				default:	$where	=	NULL;
			}
		}
		return	$where;
	}/*}}}*/

	/**
	 * assemble sql sentence for record numbers
	 *
	 * @param	$findRelation	Array
	 * @param	$where			String
	 * @param	$start			Int
	 * @param	$length			Int
	 * @return	String (SQL sentence)
	 */
	public static function assembleSqlSentenceForNums($findRelation, $where)/*{{{*/
	{
		if (!empty($findRelation['relation'])   &&  is_array($findRelation['relation']))
		{
			if (!empty($where)) $where  .=  ' AND ';
			$where  .=  ' ( '. implode(" ) AND ( ", $findRelation['relation']). ') ';
		}

		!empty($where)  &&  $where  =   ' WHERE '. $where;

		$sql    =   'SELECT count(*) as num '.
					'FROM '. $findRelation['findTable'].
					$where;
		return	$sql;
	}/*}}}*/

	/**
	 * assemble sql sentence for records
	 *
	 * @param	$findRelation	Array
	 * @param	$where			String
	 * @param	$start			Int
	 * @param	$length			Int
	 * @return	String (SQL sentence)
	 */
	public static function assembleSqlSentence($findRelation, $where, $start, $length)/*{{{*/
	{
		$tmpWhere	=	NULL;
		if (!empty($findRelation['relation'])   &&  is_array($findRelation['relation']))
		{
			$tmpWhere  .=  ' ( '. implode(" ) AND ( ", $findRelation['relation']). ') ';
		}

		if (!empty($where)) $where  =  $tmpWhere. ' AND '. $where;
		else	$where	=	$tmpWhere;

		!empty($where)  &&  $where  =   ' WHERE '. $where;

		$sql    =   'SELECT DISTINCT '. $findRelation['findContent'].
					'FROM '. $findRelation['findTable'].
					$where.
					'Limit '. $start. ', '. $length;
		return	$sql;
	}/*}}}*/

	/**
	 * parse xml for findRelation...
	 *
	 * @param	$type	String
	 * @return	Array
	 */
	public static function parseFindRelation($type = NULL)/*{{{*/
	{
		$xmlFile=	'/data/pms/pdes/conf/findRelationTable.xml';
		$xmlObj =	simplexml_load_file($xmlFile);
		$result	=	NULL;

		if (empty($type))	$result	=	self::object2array($xmlObj);
		elseif (isset($xmlObj->$type))
		{
			$result	=	self::object2array($xmlObj->$type);
		}
		return	$result;
	}/*}}}*/

	/**
	 * replace
	 * @param	String	$string
	 * @param	Array	$data
	 * @return	String
	 */
	public static function replaceString($string, $data)/*{{{*/
	{
		if (!is_array($data))	return	$string;

		$search	=	array_keys($data);
		$replace=	array_values($data);
		return		str_replace($search, $replace, $string);
	}/*}}}*/

	/**
	 * convert Object -> Array
	 *
	 * @param	$object	Object
	 * @return	Array
	 */
	public static function object2array($object)/*{{{*/
	{
		$result = NULL;

		if(is_array($object))
		{
			foreach($object as $key => $value)
			{
				$result[$key] = self::object2array($value);
			}
		}
		else if (is_object($object)) 
		{
			$var = get_object_vars($object);

			if($var)
			{
				foreach($var as $key => $value)
				{
					$result[$key] = self::object2array($value);
				}
            }
			else
			{
				return strval($object); // strval and everything is fine
			}
        } 
        else 
        {
            return $object;
        }

		return $result;
	}/*}}}*/

	public function update($object = NULL)
	{
		if (!empty($object)) {
			return self::$dbo['w']->save($object);
		} else {
			return self::$dbo['w']->save($this);
		}
	}

	/*
	 * by donald
	 * at 2011-10-26
	 */
    public function insert($obj = NULL)
    {
		if (!empty($obj)) {
        	return Model::$dbo['w']->insert($obj);
		} else {
        	return Model::$dbo['w']->insert($this);
		}
    }

}
?>
